package mdata

import (
	"database/sql"
	"gitee.com/dennis-mxx/gcode-common/mcontext"
	"gitee.com/dennis-mxx/gcode-common/mcore"
	"xorm.io/builder"
	"xorm.io/xorm"
)

var XORM_ENGINE_PREFIX = "mxx_xorm_mysql_engine"
var DEFAULT_ENGINE_ID = "default"

type QueryParam struct {
	Cols   []string
	Page   mcore.CmdPage
	Sorter *Sorter
	Cond   any
}

type Mapper[M any] struct {
	Handler XormHandler
	Mtx     *mcontext.MContext
	Inst    M
	Convert func(fieldName string) string
	Ent     Entity
}

func NewMapper[M any](mtx *mcontext.MContext, inst M) Mapper[M] {
	entity := ToEntity(inst)
	handler := WithContext(entity.DB(), mtx)
	return Mapper[M]{
		Handler: handler,
		Mtx:     mtx,
		Convert: func(fieldName string) string {
			return handler.Engine().GetTableMapper().Obj2Table(fieldName)
		},
		Ent:  entity,
		Inst: inst,
	}
}
func DefaultMapper[M any](mtx *mcontext.MContext, inst M) Mapper[M] {
	entity := ToEntity(inst)
	handler := WithContext(entity.DB(), mtx)
	return Mapper[M]{
		Handler: handler,
		Mtx:     mtx,
		Ent:     entity,
		Inst:    inst,
		Convert: func(fieldName string) string {
			return handler.Engine().GetTableMapper().Obj2Table(fieldName)
		},
	}
}
func (ew Mapper[M]) SelectPageWatch(param QueryParam) *mcore.Pageable {
	return mcore.Watch(ew.SelectPage(param))
}

func (ew Mapper[M]) SelectPage(param QueryParam) (*mcore.Pageable, error) {
	return StartPage[M](ew.Ent, ew.Inst, ew.Handler, ew.Mtx, param.Page).Sorter(ew.Convert).Cols(param.Cols).Where(param.Cond).Find()
}

func (ew Mapper[M]) Session() *xorm.Session {
	return ew.Handler.Session(ew.Mtx)
}
func (ew Mapper[M]) SelectByID(id any) (*M, error) {

	var bean M
	if exists, err := ew.Session().ID(id).Get(&bean); err == nil {
		if exists {
			return &bean, nil
		} else {
			return nil, nil
		}
	} else {
		return nil, err
	}
}

func (ew Mapper[M]) SelectByIDWatch(id any) *M {
	return mcore.Watch(ew.SelectByID(id))
}

func (ew Mapper[M]) SelectList(param QueryParam) ([]M, error) {
	ses := ew.Session().Table(ew.Ent)
	if param.Sorter != nil {
		for i, _ := range param.Sorter.descList {
			desc := param.Sorter.descList[i]
			if desc.sortType == "desc" {
				ses.Desc(desc.fieldName)
			} else {
				ses.Asc(desc.fieldName)
			}
		}
	}
	if len(param.Cols) > 0 {
		ses.Cols(param.Cols...)
	}

	beanList := make([]M, 0)
	var err error
	if param.Cond == nil {
		err = ses.Find(&beanList)
	} else {
		err = ses.Where(param.Cond).Find(&beanList)
	}
	if err != nil {
		return nil, err
	} else {
		return beanList, nil
	}

}
func (ew Mapper[M]) Count(param QueryParam) (int64, error) {
	ses := ew.Session()
	if param.Cond == nil {
		return ses.Table(ew.Ent).Count()
	} else {
		return ses.Table(ew.Ent).Where(param.Cond).Count()
	}
}

func (ew Mapper[M]) CountWatch(param QueryParam) int64 {
	ses := ew.Session()
	if param.Cond == nil {
		return mcore.Watch(ses.Table(ew.Ent).Count())
	} else {
		return mcore.Watch(ses.Table(ew.Ent).Where(param.Cond).Count())
	}
}
func (ew Mapper[M]) SelectListWatch(param QueryParam) []M {
	return mcore.Watch(ew.SelectList(param))
}

func (ew Mapper[M]) Get(entity M) (*M, error) {
	if exists, err := ew.Session().Table(ew.Ent).Get(&entity); err == nil {
		if exists {
			return &entity, nil
		} else {
			return nil, nil
		}
	} else {
		return nil, err
	}
}
func (ew Mapper[M]) WatchGet(entity M) *M {
	return mcore.Watch(ew.Get(entity))
}

func (ew Mapper[M]) UpdateByIdWatch(id any, bean M) int64 {
	return mcore.Watch(ew.UpdateById(id, bean))
}
func (ew Mapper[M]) UpdateById(id any, bean M) (int64, error) {
	return ew.Session().Table(ew.Ent).ID(id).Update(bean)
}

func (ew Mapper[M]) Insert(bean []any) (int64, error) {
	return ew.Session().Table(ew.Ent).Insert(bean...)
}

func (ew Mapper[M]) InsertWatch(bean []any) int64 {
	return mcore.Watch(ew.Insert(bean))
}

func (ew Mapper[M]) InsertMulti(rowsSlicePtr interface{}) (int64, error) {
	return ew.Session().Table(ew.Ent).InsertMulti(rowsSlicePtr)
}

func (ew Mapper[M]) InsertMultiWatch(rowsSlicePtr []any) int64 {
	return mcore.Watch(ew.Insert(rowsSlicePtr))
}

func (ew Mapper[M]) DeleteByID(tableIdValue []any, user mcore.SPUser) (bool, error) {
	num, err := ew.Delete(builder.In(ew.Ent.IdName(), tableIdValue...), user)
	return num > 0, err
}

func (ew Mapper[M]) DeleteByIDWatch(tableIdValue []any, user mcore.SPUser) bool {
	return mcore.Watch(ew.DeleteByID(tableIdValue, user))
}
func (ew Mapper[M]) Delete(cond any, user mcore.SPUser) (int64, error) {
	t := mcore.NowLocalDateTime()
	return ew.Session().Table(ew.Ent).
		Where(cond).
		Update(map[string]any{
			"deleted":          t,
			"update_time":      t,
			"update_user_code": user.UserCode,
			"update_user_name": user.UserName,
		})
}

func (ew Mapper[M]) DeleteWatch(cond any, user mcore.SPUser) int64 {
	return mcore.Watch(ew.Delete(cond, user))
}

func (ew Mapper[M]) Exec(sqlOrArgs ...interface{}) (sql.Result, error) {
	return ew.Session().Exec(sqlOrArgs...)
}
func (ew Mapper[M]) ExecWatch(sqlOrArgs ...interface{}) sql.Result {
	return mcore.Watch(ew.Exec(sqlOrArgs...))
}
